package templateDesigner
import org.grails.plugins.wsclient.service.WebService
import org.openehr.am.archetype.Archetype
import org.openehr.am.archetype.assertion.*
import org.openehr.rm.support.identification.ArchetypeID
import org.openehr.am.archetype.constraintmodel.*
import org.openehr.am.archetype.ontology.ArchetypeOntology
import org.openehr.am.archetype.constraintmodel.primitive.*
import org.openehr.am.openehrprofile.datatypes.text.*
import archetypes.*

// http://www.openehr.org/wiki/display/projects/Java+ADL+Parser+Guide
import se.acode.openehr.parser.*
import java.util.regex.Pattern
 
class ArchetypeService {
    
    static transactional = true
    WebService webService
    Archetype archetype = null
    String EXTENSION = ".adl"
    private String archetypeRepositoryPath = "ehr"
     
    def getJerarquiaDetallesArtefacto() {
        
        def wsdlURL = 'http://localhost:8080/ArtefactRepository-OpenEHRGen/services/archetype?wsdl'
        def proxy = webService.getClient(wsdlURL)

        def result = proxy.getJerarquiaDetallesArtefacto()       
        return result 
    }
        
    def getArquetipo(String n, String v, String p) {
        
        def wsdlURL = 'http://localhost:8080/ArtefactRepository-OpenEHRGen/services/archetype?wsdl'
        def proxy = webService.getClient(wsdlURL)

        def result = proxy.getArquetipo(n,v,p)       
        return result 
    }
    
    def Nodo archetype2Nodo(String archetypeStr){
        Nodo nodo = null
        
        // PARSEAR ARQUETIPO
        ADLParser parser = null;
        try { 
            parser = new ADLParser( archetypeStr )   
            archetype = parser.archetype()
            nodo = this.getNodo(archetype.getDefinition())			               		   
        }
        catch (Exception e) { print e.message }
                       		   
        return nodo 
    }
    
    /***************** Auxiliares ******************/		
    private Nodo getNodo(CObject cobject){
        Nodo nodoPadre = null
        Nodo nodoHijo = null
						
        if(cobject instanceof CComplexObject){			
            nodoPadre = this.cObjectToNodo (cobject)		
			
            ((CComplexObject)cobject).getAttributes().each { attribute ->
                //if(!(nodoPadre.nombre.toUpperCase() == "OBSERVATION" && attribute.getRmAttributeName().toUpperCase() == "STATE")){
                            
                attribute.getChildren().each { children ->					
                    nodoHijo = this.getNodo(children)
                    if(nodoHijo != null){
                        this.filterNode(nodoPadre,nodoHijo)
                    }													
                }
                //}
            }																			
        } else if(cobject instanceof CPrimitiveObject){
            //nodoPadre = this.cObjectToNodo (cobject)				
        } else if (cobject instanceof CReferenceObject){
            nodoPadre = this.cObjectToNodo (cobject)
            if(cobject instanceof ArchetypeSlot){
                Set<Assertion> assertions = ((ArchetypeSlot)cobject).getIncludes()
                InfoArchetypeSlot infoArchetypeSlot = new InfoArchetypeSlot()                
                assertions.each {
                    ExpressionBinaryOperator exItem = (ExpressionBinaryOperator)(it.getExpression())
                    ExpressionLeaf exLeaf = (ExpressionLeaf)(exItem.getRightOperand())
                    CString cstring = (CString)(exLeaf.getItem())
                    String pattern = cstring.getPattern()                     
                    String [] patterns = pattern.split("\\|")                    
                    String fileName = patterns[0].replaceAll("\\\\.","\\.")
                    def id = new ArchetypeID(fileName) 
                    def type = id.rmEntity
                    def attrs = this.splitNameFile(fileName)
                    infoArchetypeSlot.path = this.archetypeRepositoryPath + "+" + getTypePath(type)
                    infoArchetypeSlot.nombreArchivo = attrs.prefix + "." + attrs.name
                    infoArchetypeSlot.numVersion = attrs.version != null && attrs.version[0] == 'v' ? attrs.version.substring(1) : attrs.version                                                                                       
                    nodoPadre.info = infoArchetypeSlot
                }
            }   	
        } else if (cobject instanceof CDomainType){
            nodoPadre = this.cObjectToNodo (cobject) 
            if(cobject instanceof CCodePhrase){
                CCodePhrase ccp = ((CCodePhrase)cobject)
                InfoCCodePhrase infoCCodePhrase = new InfoCCodePhrase()
                infoCCodePhrase.terminologyID = ccp.terminologyId
                ccp.codeList.each {
                    String texto
                    if( archetype.getOntology().termDefinition("es",it) != null){
                        texto =  archetype.getOntology().termDefinition("es",it).getText()
                    } else if( archetype.getOntology().termDefinition("en",it) != null){
                        texto =  archetype.getOntology().termDefinition("en",it).getText()
                    }
                    
                    infoCCodePhrase.codeList[it] = texto
                }
                nodoPadre.info = infoCCodePhrase
            }
        }					
											
        return nodoPadre
    }
	
    private Nodo cObjectToNodo(CObject cobject){
        Nodo nodo = new Nodo()
		 
        nodo.identif = cobject.getNodeID()
        if(nodo.identif != null){
            if( archetype.getOntology().termDefinition("es",nodo.identif) != null){						
                nodo.nombre =  archetype.getOntology().termDefinition("es",nodo.identif).getText()
            } else if( archetype.getOntology().termDefinition("en",nodo.identif) != null){
                nodo.nombre =  archetype.getOntology().termDefinition("en",nodo.identif).getText()
            }
        }
        nodo.path = cobject.path()
        nodo.tipo = cobject.getRmTypeName()
				
        return nodo
    }
	
    private void filterNode(Nodo nodoPadre, Nodo nodoHijo){		
        if(nodoHijo.tipo.toLowerCase() == "element" || nodoHijo.tipo.toLowerCase() == "codephrase"){
            nodoHijo.hijos.each { 
                nodoPadre.hijos.add(it)				
                it.padre = nodoPadre				
                it.identif = nodoHijo.identif
                if(it.identif != null){					
                    if( archetype.getOntology().termDefinition("es",it.identif) != null){
                        it.nombre =  archetype.getOntology().termDefinition("es",it.identif).getText()
                    } else if( archetype.getOntology().termDefinition("en",it.identif) != null){
                        it.nombre =  archetype.getOntology().termDefinition("en",it.identif).getText()
                    }
                }
				
                it.path = nodoHijo.path						
            }
            nodoPadre.info = nodoHijo.info
            nodoHijo = null
        } else if(nodoHijo.tipo.toLowerCase() == "item_list" || 
            nodoHijo.tipo.toLowerCase() == "item_tree" ||
            nodoHijo.tipo.toLowerCase() == "history"   ||
            nodoHijo.tipo.toLowerCase() == "event" || 
            nodoHijo.tipo.toLowerCase() == "item_single")  {
                                  
            nodoHijo.hijos.each {
                nodoPadre.hijos.add(it)
                it.padre = nodoPadre				
            }
            nodoHijo = null
        } 
        else {
            nodoPadre.hijos.add(nodoHijo)
            nodoHijo.padre = nodoPadre					
        }						
    }
    
    private def String getTypePath( String type )
    {
        type = type.toLowerCase()
        switch (type)
        {
            case 'cluster':
            case 'composition':
            case 'element':
            case 'section':
            case 'structure':
            return type
            break
            case 'item_tree':
            case 'item_single':
            case 'item_list':
            case 'item_table':
            return 'structure'
            break
            case 'action':
            case 'evaluation':
            case 'instruction':
            case 'observation':
            return 'entry+'+type
            break
            case 'admin_entry':
            return 'entry+'+type
            break
            default:
            throw new Exception('Tipo no conocido ['+ type +'], se espera uno de: cluster, composition, element, section, item_tree, item_single, item_list, item_table, action, observation, instruction, evaluation' )
        }
    }
    
    private Map splitNameFile(String fileName){
        String [] strs = fileName.split("\\.")
        def attrs = null
        
        if(strs != null && strs.size() == 3){
            attrs = [prefix:strs[0],name:strs[1],version:strs[2]]
        }                    
        
        return attrs
    }
    
    
}